package gov.jyy.operationlog.core;


import com.fasterxml.jackson.databind.ObjectMapper;
import gov.jyy.operationlog.core.annotation.SxsqJbxxId;
import gov.jyy.operationlog.core.annotation.UserId;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class DefaultOperationLogHandle implements OperationLogHandle {

    // 发送失败的日志在内存中缓冲等待下次重试
    private static final BlockingQueue<LogContent> queue = new ArrayBlockingQueue<>(1000);

    private ApplicationContext applicationContext;

    private SendLogInterface sendLogInterface;

    private final ObjectMapper objectMapper;

    private HttpServletRequest request;

    private final Logger logger = LoggerFactory.getLogger(DefaultOperationLogHandle.class);


    public DefaultOperationLogHandle() {
        this.objectMapper = new ObjectMapper();

    }

    public DefaultOperationLogHandle(SendLogInterface sendLogInterface) {
        this();
        this.sendLogInterface = sendLogInterface;
    }

    public DefaultOperationLogHandle(SendLogInterface sendLogInterface, HttpServletRequest request) {
        this(sendLogInterface);
        this.request = request;
    }

    public DefaultOperationLogHandle(SendLogInterface sendLogInterface, HttpServletRequest request, ApplicationContext context) {
        this(sendLogInterface, request);
        this.applicationContext = context;
    }

    @Override
    public void handleLog(ProceedingJoinPoint joinPoint, Throwable throwable) {
        LogContent logContent = buildLog(joinPoint, throwable);
        if (logContent == null) {
            return;
        }
        LogSendExecutor.EXECUTOR.execute(() -> {
            // 处理掉一个之前发送失败的日志
            handleQueue();
            // 发送当前的日志
            sendLog(logContent);
        });
    }

    private void sendLog(LogContent logContent) {
        try {
            if (!sendLogInterface.sendLog(logContent)) {
                queue.add(logContent);
            }
        } catch (Exception e) {
            queue.add(logContent);
            e.printStackTrace();
        }
    }

    private void handleQueue() {
        LogContent logContent = queue.poll();
        try {
            if (logContent != null && (!sendLogInterface.sendLog(logContent))) {
                queue.add(logContent);
            }
        } catch (Exception e) {
            queue.add(logContent);
            logger.error("日志重发失败={}", logContent);
        }
    }


    @Override
    public LogContent buildLog(ProceedingJoinPoint joinPoint, Throwable throwable) {
        // 获取操作内容和类型
        OperationLog operationLog;
        Signature signature = joinPoint.getSignature();
        Class declaringType = signature.getDeclaringType();
        String name = joinPoint.getSignature().getName();
        // 获取方法参数类型
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Class[] parameterTypes = methodSignature.getParameterTypes();
        Method method;
        try {
            method = declaringType.getMethod(name, parameterTypes);
            operationLog = method.getAnnotation(OperationLog.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            // 方法不存在，处理异常情况
            return null;
        }
        if (operationLog == null) {
            return null;
        }

        final Object[] args = joinPoint.getArgs();

        LogContent logContent = new LogContent();

        LogContentInterface contentFunction = null;
        try {
            contentFunction = applicationContext.getBean(operationLog.content());
        } catch (BeansException e) {
            logger.warn("从容器中获取bean失败", e);
        }
        try {
            if(contentFunction == null) {
                contentFunction = operationLog.content().getDeclaredConstructor().newInstance();
            }

            // 有先从方法中获取 sxsqJbxxId、userId


            String sxsqJbxxId = contentFunction.getSxsqJbxxId(args);
            if (sxsqJbxxId == null || sxsqJbxxId.isEmpty()) {
                sxsqJbxxId = getValueByAnnotation(args, method, SxsqJbxxId.class);
            }
            logContent.setSxsqjbxxId(sxsqJbxxId);

            String userId = contentFunction.getUserId(args);
            if (userId == null || userId.isEmpty()) {
                userId = getValueByAnnotation(args, method, UserId.class);
            }
            logContent.setUserId(userId);

        } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                 NoSuchMethodException e) {
            logger.error("LogContentInterface接口创建失败");
        } finally {
            String operationContent;
            if (operationLog.content() != null && contentFunction != null && !operationLog.content().equals(OperationLog.DefaultLogContentInterface.class)) {
                operationContent = contentFunction.getLogContent(args);
            } else {
                operationContent = operationLog.value();
            }
            logContent.setContent(operationContent);
        }

        String argsString = null;
        try {
            argsString = getParametersJsonString(joinPoint);
        } catch (Exception e) {
            logger.error("参数解析失败");
        }

        logContent.setArgs(argsString);
        logContent.setException(throwable != null ? getStackTrace(throwable) : "");
        logContent.setOperationType(operationLog.type().getType());
        logContent.setMethod(method.toString());
        logContent.setIp(request.getRemoteAddr());
        logContent.setCreatedTime(new Date());
        return logContent;
    }


    private String getValueByAnnotation(final Object[] args, Method method, Class<? extends Annotation> annotationClass) {
        String value = null;
        // 获取参数中带有SxsqJbxxId注解的值
        for (Object arg : args) {
            if (arg != null) {
                // 注解写在形参
                if(hasAnnotationsInParameter(method, annotationClass)) {
                    value = (String) args[paramsIndex(method, annotationClass)];
                    break;
                }
                // 注解写在对象属性上
                if(hasOAnnotationsInObject(arg.getClass(), annotationClass)) {
                    value = (String) getAnnotationFieldValue(arg, annotationClass);
                    if (value != null) {
                        break;
                    }
                }

            }
        }
        return value;
    }


    private int paramsIndex(Method method, Class<? extends Annotation> annotationClass) {
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        for (int i = 0; i < parameterAnnotations.length; i++) {
            for (Annotation annotation : parameterAnnotations[i]) {
                if (annotation.annotationType().equals(annotationClass)) {
                    return i;
                }
            }
        }
        return -1;
    }

    private boolean hasAnnotationsInParameter(Method method, Class<? extends Annotation> annotationClass) {
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        // 注解使用在参数上的情况
        for (Annotation[] parameterAnnotation : parameterAnnotations) {
            for (Annotation annotation : parameterAnnotation) {
                if (annotation.annotationType().equals(annotationClass)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean hasOAnnotationsInObject(Class<?> clazz, Class<? extends Annotation> annotationClass) {
        // 注解使用在对象属性的情况
        if (clazz.getAnnotation(annotationClass) != null || clazz.isAnnotationPresent(annotationClass)) {
            return true;
        }

        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (field.getAnnotation(annotationClass) != null) {
                return true;
            }
        }
        return false;
    }

    private Object getAnnotationFieldValue(Object param, Class<? extends Annotation> annotationClass) {
        if (param == null) {
            return null;
        }

        Class<?> clazz = param.getClass();
        if (clazz.getAnnotation(annotationClass) != null) {
            // 如果参数本身就是被 SxsqJbxxId 注解修饰的对象
            return param;
        } else if (param instanceof String) {
            // 如果参数是一个字符串类型
            return null;  // 字符串类型不会包含 SxsqJbxxId 注解，返回 null
        } else {
            // 如果参数是一个对象类型
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (field.getAnnotation(annotationClass) != null) {
                    String fieldName = field.getName();
                    return getFieldValue(param, fieldName);
                }
            }
        }

        return null;
    }


    private Object getFieldValue(Object obj, String fieldName) {
        if (obj == null || fieldName == null || fieldName.isEmpty()) {
            return null;
        }

        try {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(obj);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            logger.error("获取字段值失败: {}", e.getMessage(), e);
            return null;
        }
    }


    public static String getStackTrace(Throwable throwable) {
        StringWriter sw = new StringWriter();

        try (PrintWriter pw = new PrintWriter(sw)) {
            throwable.printStackTrace(pw);
            return sw.toString();
        } catch (Exception e) {
            return null;
        }
    }


    /**
     * 方法参数转json字符串
     */
    private String getParametersJsonString(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        List<Object> modifiedArgs = new ArrayList<>();

        for (Object arg : args) {
            if (arg instanceof MultipartFile) {
                MultipartFile file = (MultipartFile) arg;
                // 获取上传的文件名称作为参数值
                String fileName = file.getOriginalFilename();
                modifiedArgs.add(fileName);
            } else if (!(arg instanceof ServletRequest) && !(arg instanceof ServletResponse)) {
                modifiedArgs.add(arg);
            }
        }

        try {
            return objectMapper.writeValueAsString(modifiedArgs);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


}
